#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "raylib.h"
#include "pub_lib.h"
#include "dpi_algo.h"

const int screenWidth = 1024;
const int screenHeight = 768;

Image cat;
Texture2D gtexture;
char* hint_str = NULL;
static my_image effect_data;
run_state gstate = STATE_IDLE;

static float scale_coff = 1.0f;
static float rotate_angle = 0.0f;

const char command_str_en[7][MAX_HINT_STRING_LEN] =
{
	"1. translation",
	"2. mirror: horizontal",
	"3. mirror: vertical",
	"4. transpose",
	"5. scale down",
	"6. scale up",
	"7. rotate",
};

const char command_str_cn[7][MAX_HINT_STRING_LEN] =
{
	"1. 图像平移",
	"2. 水平镜像",
	"3. 垂直镜像",
	"4. 图像转置",
	"5. 图像缩小",
	"6. 图像放大",
	"7. 图像旋转",
};

void init_effect(Image image)
{
	effect_data.ori = image;
	effect_data.order = 0;

	effect_data.chg = ImageCopy(image);
	effect_data.image_size = GetPixelDataSize(image.width, image.height,\
		image.format);
	effect_data.pix_len = get_pixel_bytes(image.width, image.height,\
		effect_data.image_size);
	memset(effect_data.chg.data, 255, effect_data.image_size);
	effect_data.texture = LoadTextureFromImage(effect_data.chg);
}

void reset_effect_data(void)
{
	int size = effect_data.image_size;
	memset(effect_data.chg.data, 255, size);
}

void reset_effect_private(void)
{
	effect_data.order = 1;
	effect_data.start_pos = 0;
}

void reset_effect_all(void)
{
	reset_effect_private();
	reset_effect_data();
}

void destroy_effect(void)
{
	UnloadImage(effect_data.chg);
	UnloadTexture(effect_data.texture);
}

void translation(my_image* ptr, int x_offset, int y_offset)
{
	hint_str = "1. translation. press direction key";
	if (x_offset == 0 && y_offset == 0) {
		//just show original image
		ptr->start_pos = 0; //x offset
		ptr->order = 0; //y offset
		memcpy(ptr->chg.data, effect_data.ori.data, ptr->image_size);
		UpdateTexture(effect_data.texture, effect_data.chg.data);
		return;
	}

	ptr->start_pos += x_offset;
	ptr->order += y_offset;
	//limit: max move 50
	int x, y, max;
	x = abs(ptr->start_pos);
	y = abs(ptr->order);
	max = y;
	if (x > y)
		max = x;
	if (max > 50) {
		//restore
		ptr->start_pos -= x_offset;
		ptr->order -= y_offset;
		return;
	}

	printf("ongoing %s\n", command_str_cn[0]);
	gm_translation(&ptr->ori, &ptr->chg, ptr->pix_len, ptr->start_pos, ptr->order);
	gstate = STATE_WAIT_NEXT;
	UpdateTexture(effect_data.texture, effect_data.chg.data);
}

void check_scale(void)
{
	if (scale_coff == 1.0f) {
		return;
	}
	if (scale_coff > 2.0f) {
		scale_coff = 2.0f;
		return;
	}
	if (scale_coff < 0.5f) {
		scale_coff = 0.5f;
		return;
	}
	memcpy(effect_data.chg.data, effect_data.ori.data, effect_data.image_size);
	UpdateTexture(effect_data.texture, effect_data.chg.data);
	gstate = STATE_FINISH;
}

void transpose(my_image* ptr)
{
	gm_transpose(&ptr->ori, &ptr->chg, ptr->pix_len);
	UpdateTexture(effect_data.texture, effect_data.chg.data);
}

void special_effect(int cmd, int startx, int starty)
{
	if (cmd <= 7)
		hint_str = (char*)command_str_en[cmd-1];
	if(cmd!= 5 && cmd!= 6) //scale down up
		scale_coff = 1.0f;
	if(cmd != 7)
		rotate_angle = 0.0f;

	switch(cmd) {
		case 1:
			translation(&effect_data, startx, starty);
			break;
		case 2:
			gm_mirror(&effect_data.ori, &effect_data.chg, effect_data.pix_len, 1);
			UpdateTexture(effect_data.texture, effect_data.chg.data);
			break;
		case 3:
			gm_mirror(&effect_data.ori, &effect_data.chg, effect_data.pix_len, 0);
			UpdateTexture(effect_data.texture, effect_data.chg.data);
			break;
		case 4:
			transpose(&effect_data);
			break;
		case 5:
			scale_coff -= 0.1f;
			check_scale();
			break;
		case 6:
			scale_coff += 0.1f;
			check_scale();
			break;
		case 7:
			rotate_angle += 90.0f;
			if(rotate_angle >= 360.0f)
				rotate_angle = 0.0f;
			memcpy(effect_data.chg.data, effect_data.ori.data, effect_data.image_size);
			UpdateTexture(effect_data.texture, effect_data.chg.data);
			gstate = STATE_FINISH;
			break;
		default:
			scale_coff = 1.0f;
			hint_str = "not support command";
			reset_effect_data();
			UpdateTexture(effect_data.texture, effect_data.chg.data);
			break;
	}
}

void handle_dir_key(int key, int *x, int *y)
{
	switch(key) {
		case KEY_LEFT:
			*x = -10;
			*y = 0;
			break;
		case KEY_RIGHT:
			*x = 10;
			*y = 0;
			break;
		case KEY_UP:
			*x = 0;
			*y = -10;
			break;
		case KEY_DOWN:
			*x = 0;
			*y = 10;
			break;
		default:
			*x = 0;
			*y = 0;
			break;
	}
}

int main(int argc, char** argv)
{
	// Initialization
	//--------------------------------------------------------------------------------------

	char* file_path = mm_path;
	if (argc < 2) {
		printf("usage: %s image_file \n", argv[0]);
	}
	else {
		file_path = argv[1];
	}
	InitWindow(screenWidth, screenHeight, "几何变换");

	// NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
	printf("open file %s\n", file_path);
	cat = LoadImage(file_path);             // Load image in CPU memory (RAM)
	if (!cat.data)
		return -1;

	gtexture = LoadTextureFromImage(cat);      // Image converted to texture, uploaded to GPU memory (VRAM)
	init_effect(cat);

	SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
	print_info_cn(command_str_cn, 7);
	//--------------------------------------------------------------------------------------

	int startx = 10;
	int starty = 30;
	int right_startx = startx + 20 + gtexture.width;
	int right_image_startx = right_startx + 20;
	int right_image_starty = starty;

	int key = 0;
	int change = 0;
	Vector2 mouse = GetMousePosition();
	int offsetx = 0, offsety = 0;
	int effect_command = 0; //1 - 7 is valid

	// Main game loop
	while (!WindowShouldClose())    // Detect window close button or ESC key
	{
		// Update
		key = GetKeyPressed();
		if (key >= '1' && key <= '9') {
			change = 1;
			key = key - '0';
			effect_command = key;
		} else if(key >= KEY_RIGHT && key <= KEY_UP) {
			change = 1;
			handle_dir_key(key, &offsetx, &offsety);
		} else if (key == ' ') {
			if (gstate == STATE_WAIT_NEXT)
				gstate = STATE_ONGOING;
		} else if (key == ESC_KEY) {
			break;
		}

		if (change) {
			change = 0;
			hint_str = "number key pressed";
			if (effect_command == 1) {
				//平移
				special_effect(effect_command, offsetx, offsety);
				gstate = STATE_WAIT_NEXT;
			} else {
				special_effect(key, offsetx, offsety);
			}
		} else if (gstate == STATE_ONGOING) {
			special_effect(effect_command, 0, 0);
		}
		//----------------------------------------------------------------------------------
		//----------------------------------------------------------------------------------

		// Draw
		//----------------------------------------------------------------------------------
		BeginDrawing();
		ClearBackground(RAYWHITE);

		if (hint_str) {
			DrawText(hint_str, right_image_startx, 0, 20, BLACK);
		}
		DrawTexture(gtexture, startx, starty, WHITE);
		show_info_en(command_str_en, 7, startx, gtexture.height + starty + 20);
		DrawLine(right_startx, starty, right_startx, GetScreenHeight(), GRAY);
		if(rotate_angle == 0.0f)
			DrawTextureEx(effect_data.texture,\
				(Vector2){ right_image_startx, right_image_starty },\
				0.0f, scale_coff, WHITE);
		else
			DrawTextureEx(effect_data.texture,\
				(Vector2){ right_image_startx+300, right_image_starty+300},\
				rotate_angle, scale_coff, WHITE);


		if (gstate == STATE_WAIT_NEXT) {
			DrawText("press space key to reset", \
				GetScreenWidth() - 800, GetScreenHeight() - 50, 20, BLACK);
		}

		mouse = GetMousePosition();
		DrawText(TextFormat("mouse(%d, %d)", (int)mouse.x, (int)mouse.y), \
			GetScreenWidth() - 240, GetScreenHeight() - 50, 20, BLACK);

		EndDrawing();
		//----------------------------------------------------------------------------------
	}

	// De-Initialization
	destroy_effect();
	UnloadImage(cat);
	UnloadTexture(gtexture);       // Texture unloading

	//--------------------------------------------------------------------------------------
	CloseWindow();        // Close window and OpenGL context
	//--------------------------------------------------------------------------------------

	return 0;
}


